/**
 * UserInterface.java
 * Purpose: Creates the user interface for the sundial program.
 * @author Shaun Takeuchi
 */

import java.awt.*;
import javax.swing.*;
import java.awt.event.*;
import java.util.Calendar;
import java.net.URI;
import java.net.URISyntaxException;
import java.io.IOException;

@SuppressWarnings("serial")
public class UserInterface extends JFrame 
{
   private static final int WIDTH = 400;
   private static final int HEIGHT = 200;
   
   // frame and components of frame
   private JFrame frame;
   private JMenuBar menuBar;
   private JMenu menu;
   private JMenuItem menuItem;
   private JPopupMenu clickMenu;
   private JLabel latitudeL, longitudeL, dateL, timezoneL; 
   private JTextField latitudeTF, longitudeTF; 
   private JButton drawB, exitB, findB; 
   private JTextArea topText;
   private JComboBox<String> monthCB, dayCB, yearCB, timezoneCB; //drop down
   private DefaultComboBoxModel<String> dayCBM;
   private JCheckBox daylightcb;
   
   private Calendar today;
   
   //event handlers
   private ResetMenuHandler resetHandler;
   private RightClickHandler clickHandler;
   private CutHandler cutHandler;
   private CopyHandler copyHandler;
   private PasteHandler pasteHandler;
   private DrawButtonHandler drawHandler;
   private ExitHandler exitHandler;
   private FindButtonHandler findHandler;
   private DayHandler dayHandler;

   
   private String[] months = {"January","February","March","April","May","June","July",
      "August","September","October","November","December"};
   private String[] days,years;
   private String[] timezones = {"GMT -11:00","GMT -10:00", "GMT -9:00","GMT -8:00", 
      "GMT -7:00","GMT -6:00", "GMT -5:00","GMT -4:30","GMT -4:00","GMT -3:30", "GMT -3:00","GMT -2:00", 
      "GMT -1:00","GMT","GMT +1:00","GMT +2:00", "GMT +3:00", "GMT +3:30","GMT +4:00", "GMT +4:30", 
      "GMT +5:00", "GMT +5:30", "GMT +5:45","GMT +6:00","GMT +6:30", "GMT +7:00","GMT +8:00", 
      "GMT +9:00","GMT +9:30","GMT +10:00", "GMT +11:00","GMT +12:00"};
   private double[] standardMeridian = {-165,-150,-135,-120,-105,-90,-75,-60,-60,-45,-45,-30,-15,0,
      15,30,45,45,60,60,75,75,75,90,90,105,120,135,135,150,165,180};
   
   /**
    * constructor for UserInterface class.
    * initializes all the frame, menu and pane.
    */
   public UserInterface()
   {
      // initializes days and years and gets current date
     createDates(); 
     
     // initialize JFrame
     frame = new JFrame("Sundial Creator");
      
      menuBar = new JMenuBar();

      //Build the menu.
      menu = new JMenu("File");
      menuBar.add(menu);
      
      // initializes menuItems for menu bar
      menuItem = new JMenuItem("Reset");
      resetHandler = new ResetMenuHandler();
      menuItem.addActionListener(resetHandler);
      menu.add(menuItem);
      menuItem = new JMenuItem("Exit");
      exitHandler = new ExitHandler();
      menuItem.addActionListener(exitHandler);
      menu.add(menuItem);
      
      // initializes menuItems for right click
      clickMenu = new JPopupMenu();
      menuItem = new JMenuItem("Cut");
      cutHandler = new CutHandler();
      menuItem.addActionListener(cutHandler);
      clickMenu.add(menuItem);
      menuItem = new JMenuItem("Copy");
      copyHandler = new CopyHandler();
      menuItem.addActionListener(copyHandler);
      clickMenu.add(menuItem);
      menuItem = new JMenuItem("Paste");
      pasteHandler = new PasteHandler();
      menuItem.addActionListener(pasteHandler);
      clickMenu.add(menuItem);    
      
      //giving all the objects values
      topText = new JTextArea("Welcome to the sundial creator.  This program can create "
         + "a\nworking sundial print out using the latitude, longitude, date of\nuse and timezone to work properly. \n" 
         + "As a warning this program does support a latitude below \n-68 and above 68 degrees, but the gnomon is unable to fit on \none page.  "
         + "This program is also unable to create a sundial \nnear the equator.");
      topText.setLineWrap(true);
      topText.setEditable(false);      
      latitudeL = new JLabel("Latitude: ");
      longitudeL = new JLabel("Longitude: ");
      dateL = new JLabel("Date of use: ");
      timezoneL = new JLabel("Timezone: ");
      
      //setting length of input
      latitudeTF = new JTextField(10);
      clickHandler = new RightClickHandler();
      latitudeTF.addMouseListener(clickHandler);
      latitudeTF.setText("0.00000");
      longitudeTF = new JTextField(10);
      longitudeTF.addMouseListener(clickHandler);
      longitudeTF.setText("0.00000");
      
      //buttons
      drawB = new JButton("Draw");
      drawHandler = new DrawButtonHandler();
      drawB.addActionListener(drawHandler);
      exitB = new JButton("Exit");
      exitB.addActionListener(exitHandler);
      findB = new JButton("Find my coordinates");
      findHandler = new FindButtonHandler();
      findB.addActionListener(findHandler);
      
      //drop down
      monthCB = new JComboBox<String>(months);
      monthCB.setSelectedIndex(today.get(Calendar.MONTH));
      dayHandler = new DayHandler();
      monthCB.addActionListener(dayHandler);
      dayCBM = new DefaultComboBoxModel<String>(days);
      dayCB = new JComboBox<String>();
      dayCB.setModel(dayCBM);
      dayCB.setSelectedIndex(today.get(Calendar.DATE)- 1);
      yearCB = new JComboBox<String>(years);
      yearCB.addActionListener(dayHandler);
      timezoneCB = new JComboBox<String>(timezones);
      timezoneCB.setSelectedIndex(1);
      
      //checkbox
      daylightcb = new JCheckBox("Daylight Savings");
      
      
      //Create the pane, set layout type and create a constraint variable for pane
      Container pane = getContentPane();
      pane.setLayout(new GridBagLayout());
      GridBagConstraints constraints = new GridBagConstraints();
      
      //adding onto the pane     
      constraints.fill = GridBagConstraints.HORIZONTAL;
      constraints.gridx = 0;       // x coordinate
      constraints.gridy = 1;       // y coordinate
      constraints.gridwidth = 1;   //width of area
      pane.add(latitudeL, constraints);
      
      constraints.gridwidth = 3;
      constraints.gridx = 1;
      constraints.gridy = 1;
      pane.add(latitudeTF, constraints);
      
      constraints.gridwidth = 1;
      constraints.gridx = 0;
      constraints.gridy = 2;
      pane.add(longitudeL, constraints);
      
      constraints.gridwidth = 3;
      constraints.gridx = 1;
      constraints.gridy = 2;
      pane.add(longitudeTF, constraints);
      
      constraints.gridwidth = 1;
      constraints.gridx = 0;
      constraints.gridy = 3;
      pane.add(dateL, constraints);
      
      constraints.gridx = 1;
      constraints.gridy = 3;
      pane.add(monthCB, constraints);
      
      constraints.gridx = 2;
      constraints.gridy = 3;
      pane.add(dayCB, constraints);
      
      constraints.gridx = 3;
      constraints.gridy = 3;
      pane.add(yearCB, constraints);
      
      constraints.gridx = 0;
      constraints.gridy = 4;
      pane.add(timezoneL, constraints);
      
      constraints.gridx = 1;
      constraints.gridy = 4;
      pane.add(timezoneCB, constraints);
      
      constraints.gridx = 2;
      constraints.gridy = 4;
      pane.add(daylightcb, constraints);
      
      constraints.gridwidth = 2;
      constraints.gridx = 0;
      constraints.gridy = 5;
      pane.add(drawB, constraints);
      
      constraints.gridx = 2;
      constraints.gridy = 5;
      pane.add(exitB, constraints);

      constraints.gridwidth = 4;
      constraints.gridx = 0;
      constraints.gridy = 6;
      pane.add(findB, constraints);
      
      constraints.gridx = 0;
      constraints.gridy = 0;
      pane.add(topText, constraints);
      
      // adding menubar and pane to frame
      frame.setJMenuBar(menuBar);
      frame.add(pane);
      
      // setting frame location, size, visible, packs and allows upper right x to close
      frame.setLocation(100, 100);
      frame.setSize(WIDTH, HEIGHT);
      frame.setVisible(true);
      frame.pack();
      frame.setDefaultCloseOperation(EXIT_ON_CLOSE);
   }
 
   
   /**
    * initializes an array of days in a month and array of years.
    * uses the Calendar class to get current date for initial date on user interface.
    */
   private void createDates()
   {
      today = Calendar.getInstance();
      int y;
      
      // days per month
      switch(today.get(Calendar.MONTH)) {
         case 3:
           case 5:
           case 8:
           case 10:
              y = 30;
              break;
           case 1:
              if (today.get(Calendar.YEAR) % 4 == 0)
                 y = 29; //leap year
              else
                 y = 28;
              break;
           default: 
              y = 31;
              break;
      }
      
      // fills array with days of the month
      days = new String[y];      
      for (int i = 0; i < y; i++)
      {
         days[i] = String.valueOf(i+1);
      }
      
      // fills array with current year to current year + 10
      int temp = today.get(Calendar.YEAR);
      years = new String[10];
      for (int i = 0; i < 10; i++) {
         years[i] = String.valueOf(temp++);
      }
   }
   
   /**
    * class to handle the ActionListener for the reset in the menu.
    * resets the text fields and resets the JComboBox to their original index
    */
   public class ResetMenuHandler implements ActionListener
   {
      public void actionPerformed(ActionEvent event) 
      {  
         latitudeTF.setText("0.00000");
         longitudeTF.setText("0.00000");
         monthCB.setSelectedIndex(today.get(Calendar.MONTH));
         dayCB.setSelectedIndex(today.get(Calendar.DATE) - 1);
         yearCB.setSelectedIndex(0);
         timezoneCB.setSelectedIndex(1);
         daylightcb.setSelected(false);
      }  
   }

   
   /**
    * class to handle the MouseAdapter for right click popup menu.
    */
   public class RightClickHandler extends MouseAdapter
   {
      public void mousePressed(MouseEvent event) {
         if (event.getModifiers() == InputEvent.BUTTON3_MASK) 
         {
               clickMenu.show(event.getComponent(), event.getX(), event.getY());
           }
       }
   }
   
   /**
    * class to handle the ActionListener cut in the right click popup menu
    */
   public class CutHandler implements ActionListener
   {
      public void actionPerformed(ActionEvent event) 
      {
         JTextField temp = (JTextField) clickMenu.getInvoker();
           temp.cut();
      }
   } 
   
   /**
    * class to handle the ActionListener copy in the right click popup menu
    */
   public class CopyHandler implements ActionListener
   {
      public void actionPerformed(ActionEvent event) 
      {
         JTextField temp = (JTextField) clickMenu.getInvoker();
           temp.copy();
      }
   } 
   
   /**
    * class to handle the ActionListener paste in the right click popup menu
    */
   public class PasteHandler implements ActionListener
   {
      public void actionPerformed(ActionEvent event) 
      {
         JTextField temp = (JTextField) clickMenu.getInvoker();
           temp.paste();
      }
   }
   
   
   /**
    * class to handle the ActionListener for the draw button.
    */
   private class DrawButtonHandler implements ActionListener
   {
      public void actionPerformed(ActionEvent event)
      {
         double latitude, longitude;
         int month, day, year;
         
         try {
            latitude = Double.parseDouble(latitudeTF.getText());
            longitude = Double.parseDouble(longitudeTF.getText());
            month = monthCB.getSelectedIndex() + 1;
            day = dayCB.getSelectedIndex() + 1;
            year = yearCB.getSelectedIndex() + today.get(Calendar.YEAR);
            
            double std_mer = standardMeridian[timezoneCB.getSelectedIndex()];
             
            if ((latitude >= -90 && latitude <= 90) && (longitude >= -180 && longitude <= 180))
            {
               Calculation c = new Calculation(latitude, longitude, year, month, day, std_mer, daylightcb.isSelected());
               new Drawing(c);
            }
            else
            {
               if (!(latitude >= -90 && latitude <= 90))
               {
                  JOptionPane.showMessageDialog(frame, "Error! Latitude must be between -90 and 90.", "Invalid Input", JOptionPane.ERROR_MESSAGE);
               }
               else
               {
                  JOptionPane.showMessageDialog(frame, "Error! Longitude must be between -180 and 180.", "Invalid Input", JOptionPane.ERROR_MESSAGE);
               }
            }
         }
         catch (NumberFormatException e) {
            JOptionPane.showMessageDialog(frame, "Error! Latitude and Longitude must be a number.", "Invalid Input", JOptionPane.ERROR_MESSAGE);
        }
      }
   }
   
   /**
    * class to handle the ActionListener for exiting the program.
    */
   public class ExitHandler implements ActionListener
   {
      public void actionPerformed(ActionEvent e)
      {
         System.exit(0);
      }
   }
   
   /**
    * class to handle the ActionListener for the find coordinates button
    */
   public class FindButtonHandler implements ActionListener
   {
      public void actionPerformed(ActionEvent event)
      {
         try {
            Desktop.getDesktop().browse(new URI("http://universimmedia.pagesperso-orange.fr/geo/loc.htm"));
         } 
         catch (URISyntaxException | IOException e) {
            JOptionPane.showMessageDialog(frame, "Error! Unable to find a default browser.", "No Default Web Browser", JOptionPane.ERROR_MESSAGE);
         }
      }
   }
   
   /**
    * class to handle the ActionListener for total days in a month on the given year.
    * the number of days varies by month and year.
    */
   public class DayHandler implements ActionListener
   {
      public void actionPerformed(ActionEvent event) 
      {
         int y;
         switch(monthCB.getSelectedIndex()) {
            case 3:
            case 5:
            case 8:
            case 10:
               y = 30;
               break;
            case 1:
               if ((today.get(Calendar.YEAR) + yearCB.getSelectedIndex() ) % 4 == 0)
                  y = 29;
               else
                  y = 28;
               break;
            default: 
               y = 31;
               break;
         }
         days = new String[y];      
         for (int i = 0; i < y; i++)
         {
            days[i] = String.valueOf(i+1);
         }
         int temp = dayCB.getSelectedIndex();
         dayCBM = new DefaultComboBoxModel<String>(days);
         dayCB.setModel(dayCBM);
         if (y > temp)  //max days > old day selected
            dayCB.setSelectedIndex(temp);
         else
            dayCB.setSelectedIndex(y-1);
      }  
   }
   
   public static void main(String[] args)
   {
      new UserInterface();
   }
}
